﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;

namespace KerbinCity
{
    public class BuildingGenerator
    {
        static System.Random random = new System.Random();
        private static List<Material> lowSideTexture;
        private static List<Material> midSideTexture;
        private static List<Material> highSideTexture;
        private static List<List<Material>> sideTextures;
        private static Material roofTexture;

        public class Building
        {
            public Material[] Materials;
            public Mesh Mesh;
            public Mesh ColliderMesh;

            public Building(Mesh mesh, Mesh collider, Material[] materials)
            {
                Mesh = mesh;
                Materials = materials;
                ColliderMesh = collider;
            }
        }

        private class BlockBuildingStyle
        {
            public Texture2D Texture;
            public float MinFloor;
	        public float MaxFloor;
	
	        public float MinHeight;
	        public float MaxHeight;

            public Building GenerateBuilding()
            {
                return new Building(null, null, null);
            }
        }

        public static void InitTextures()
        {
            String path =  KSPUtil.ApplicationRootPath + "GameData/Redev/Textures/sides/";
            KerbinCity.Log("Loading Textures from " +path);
            DirectoryInfo info = new DirectoryInfo(path);
            FileInfo[] fileInfo = info.GetFiles();
           
            lowSideTexture = new List<Material>();
            midSideTexture = new List<Material>();
            highSideTexture = new List<Material>();

            sideTextures = new List<List<Material>>();
            sideTextures.Add(lowSideTexture);
            sideTextures.Add(midSideTexture);
            sideTextures.Add(highSideTexture);

            foreach (FileInfo file in fileInfo)
            {
                String fileName = Regex.Match(file.Name, @"(?<name>.+)\.[^.]+$").Groups["name"].Value;
                Texture2D texture = GameDatabase.Instance.GetTexture("Redev/Textures/sides/" + fileName, false);
                KerbinCity.Log("texture: " + fileName);
                Match match = Regex.Match(fileName, @"(?<size>(low)|(mid)|(high))_(?<number>[\d]+)_(?<width>[\d]+\.?[\d]*)_(?<height>[\d]+\.?[\d]*)");
                float width = (float)float.Parse(match.Groups["width"].Value);
                float height = float.Parse(match.Groups["height"].Value);
                Material newMaterial = new Material(Shader.Find("VertexLit"));
                newMaterial.mainTexture = texture;
                newMaterial.mainTextureScale = new Vector2(200f / width, 200f / height);
                switch (match.Groups["size"].Value)
                {
                    case "low":
                        KerbinCity.Log("low Texture added");
                        lowSideTexture.Add(newMaterial);
                        break;
                    case "mid":
                        KerbinCity.Log("mid Texture added");
                        midSideTexture.Add(newMaterial);
                        break;
                    case "high":
                        KerbinCity.Log("high Texture added");
                        highSideTexture.Add(newMaterial);
                        break;
                }
                
            }

          


/*
            
            concreteSideTexture.mainTexture = GameDatabase.Instance.GetTexture("Redev/Buildings/Textures/sides/low1", false); ;
            concreteSideTexture.mainTextureScale = new Vector2(200f, 200f);
            */
            /*
            Assembly assembly = Assembly.GetExecutingAssembly();
            KerbinCity.Log(assembly.FullName);
            string[] resources = assembly.GetManifestResourceNames();
            KerbinCity.Log(resources.Length.ToString());
            foreach (string name in resources)
            {
                KerbinCity.Log(name);
            }
            
            ResourceManager rm = new ResourceManager("KerbinCity.Shaders",
                                         assembly);

            KerbinCity.Log(rm.BaseName);

            KerbinCity.Log(rm.GetString("CityEmissionLights"));
            roofTexture = new Material(rm.GetString("CityEmissionLights"));
            */
            roofTexture = new Material(Shader.Find("VertexLit"));
            //roofTexture.mainTexture = GameDatabase.Instance.GetTexture("Redev/Textures/tops/light", false);
            roofTexture.mainTexture = GameDatabase.Instance.GetTexture("Redev/Textures/tops/roof", false);
            roofTexture.mainTextureScale = new Vector2(100f, 100f);
        }

        public static Building GenerateMesh(float inX, float inY, float inWidth, float inDepth, float heightRatio)
        {

            float width = 2.5f * (float)Math.Round(inWidth / 2.5f);
            float depth = 2.5f * (float)Math.Round(inDepth / 2.5f);

            float area = width * depth;
            float maxHeight = area / 10.0f;
            maxHeight = Math.Max(maxHeight, 25);

            float inHeight = heightRatio * (10.0f + (maxHeight*(float)random.NextDouble()));

            float x = inX + ((inWidth - width) / 2.0f);
            float y = inY + ((inDepth - depth) / 2.0f);

            float height = 2.5f * (float)Math.Round(inHeight / 2.5f); 

            KerbinCity.Log("x: " + x +
                        " y: " + y +
                        " width: " + width +
                        " depth: " + depth +
                        " height: " + height);

           // KerbinCity.Log("Generating Building...");

            Mesh mesh = new Mesh();
            Mesh meshCollider = new Mesh();
            
            mesh.name = "testMesh";
            meshCollider.name = "colliderMesh";

            //KerbinCity.Log("Creating verticies...");
            mesh.Clear();
            meshCollider.Clear();
            mesh.subMeshCount = 2;
            float hwidth = x + width;
            float hdepth = y + depth;
            Vector3[] vertices = new Vector3[24] 
            {   
                //front
                new Vector3(x, height, y), // 0
                new Vector3(hwidth, height, y),      // 1
                new Vector3(x, 0.0f, y),    // 2
                new Vector3(hwidth, 0.0f, y), // 3
                //back
                new Vector3(x, height, hdepth),      // 4
                new Vector3(hwidth, height, hdepth),       // 5
                new Vector3(x, 0.0f, hdepth), // 6
                new Vector3(hwidth, 0.0f, hdepth),       // 7
                //right
                new Vector3(x, height, hdepth),      // 8
                new Vector3(x, height, y), // 9
                new Vector3(x, 0.0f, hdepth), // 10
                new Vector3(x, 0.0f, y),    // 11
                //left
                new Vector3(hwidth, height, hdepth),       // 12
                new Vector3(hwidth, height, y),      // 13
                new Vector3(hwidth, 0.0f, hdepth),       // 14
                new Vector3(hwidth, 0.0f, y),   // 15
                //top
                new Vector3(x, height, hdepth),      // 16
                new Vector3(hwidth, height, hdepth),       // 17
                new Vector3(x, height, y), // 18
                new Vector3(hwidth, height, y),      // 19
                //bottom
                new Vector3(hwidth, 0.0f, y), // 20
                new Vector3(hwidth, 0.0f, hdepth),       // 21
                new Vector3(x, 0.0f, y),    // 22
                new Vector3(x, 0.0f, hdepth) // 23
            };

            Vector3[] colliderVertices = new Vector3[8] 
            {   
                //front
                new Vector3(x, height, y), // 0
                new Vector3(hwidth, height, y),      // 1
                new Vector3(x, 0.0f, y),    // 2
                new Vector3(hwidth, 0.0f, y), // 3
                //back
                new Vector3(x, height, hdepth),      // 4
                new Vector3(hwidth, height, hdepth),       // 5
                new Vector3(x, 0.0f, hdepth), // 6
                new Vector3(hwidth, 0.0f, hdepth),       // 7
                /*
                //right
                new Vector3(x, height, hdepth),      // 8 - 4
                new Vector3(x, height, y),           // 9 - 0
                new Vector3(x, 0.0f, hdepth),        // 10 - 6
                new Vector3(x, 0.0f, y),             // 11 - 2
                //left
                new Vector3(hwidth, height, hdepth), // 12 - 5
                new Vector3(hwidth, height, y),      // 13 - 1
                new Vector3(hwidth, 0.0f, hdepth),   // 14 - 7
                new Vector3(hwidth, 0.0f, y),        // 15 - 3
                //top
                new Vector3(x, height, hdepth),      // 16 - 4
                new Vector3(hwidth, height, hdepth), // 17 - 5
                new Vector3(x, height, y),           // 18 - 0
                new Vector3(hwidth, height, y),      // 19 - 1
                //bottom
                new Vector3(hwidth, 0.0f, y),        // 20 - 3
                new Vector3(hwidth, 0.0f, hdepth),   // 21 - 7
                new Vector3(x, 0.0f, y),             // 22 - 2
                new Vector3(x, 0.0f, hdepth)         // 23 - 6
                */
            };

            mesh.vertices = vertices;
            meshCollider.vertices = colliderVertices;

            //KerbinCity.Log("Creating triangles...");
            //side triangles
            int[] triangles = new int[24]
            {   0, 1, 2,    // front
                2, 1, 3,
                7, 5, 6,    // back
                6, 5, 4,
                8, 9, 10,    // right
                10, 9, 11,
                15, 13, 14,    // left
                14, 13, 12
             };
            mesh.SetTriangles(triangles, 0);

            //top triangles...
            triangles = new int[12]
            {   16, 17, 18,    // top
                18, 17, 19,
                20, 21, 22,    // bottom
                22, 21, 23
             };
            mesh.SetTriangles(triangles, 1);

            int[] colliderTriangles = new int[36]
            {   0, 1, 2,    // front
                2, 1, 3,
                7, 5, 6,    // back
                6, 5, 4,
                4, 0, 6,    // right
                6, 0, 2,
                3, 1, 7,    // left
                7, 1, 5,
                4, 5, 0,    // top
                0, 5, 1,
                3, 7, 2,    // bottom
                2, 7, 6
             };
            meshCollider.triangles = colliderTriangles;

            //KerbinCity.Log("Updating normals/bounds...");

            float maxdimension = Math.Max(width, height);
            maxdimension = Math.Max(maxdimension, depth);
            maxdimension = 500.0f;
            float uvwidth, uvheight, uvdepth;
            uvwidth = width / maxdimension;
            uvheight = height / maxdimension;
            uvdepth = depth / maxdimension;
            mesh.uv = new Vector2[24]
            {
                //front
                new Vector2(0,uvheight),
                new Vector2(uvwidth,uvheight),
                new Vector2(0,0),
                new Vector2(uvwidth,0),
                //back
                new Vector2(0,uvheight),
                new Vector2(uvwidth,uvheight),
                new Vector2(0,0),
                new Vector2(uvwidth,0),
                //right
                new Vector2(0,uvheight),
                new Vector2(uvdepth,uvheight),
                new Vector2(0,0),
                new Vector2(uvdepth,0),
                //left
                new Vector2(0,uvheight),
                new Vector2(uvdepth,uvheight),
                new Vector2(0,0),
                new Vector2(uvdepth,0),
                //top
                new Vector2(0,uvdepth),
                new Vector2(uvwidth,uvdepth),
                new Vector2(0,0),
                new Vector2(uvwidth,0),
                //bottom
                new Vector2(0,uvdepth),
                new Vector2(uvwidth,uvdepth),
                new Vector2(0,0),
                new Vector2(uvwidth,0)
            };

            //KerbinCity.Log("Creating Materials...");
            Material[] materials = new Material[mesh.subMeshCount];
            int min = 0;
            int max = 1;
            if(height > 70)
            {
                min = 2;
                max = sideTextures.Count;
            }
            else if(height > 20)
            {
                min = 1;
                max = sideTextures.Count;
            }

            int size = random.Next(min, max);
            KerbinCity.Log("min: " + min + " size: " + size);
            KerbinCity.Log("sideTextures: " + sideTextures.Count);
            List<Material> materialList = sideTextures[size];
            KerbinCity.Log("materialList: " + materialList.Count);
            materials[0] = materialList[random.Next(materialList.Count)];
            materials[1] = roofTexture;
            
            
            return new Building(mesh, meshCollider, materials);
        }

        public static GameObject Generate(float width, float depth, float height)
        {
  
            KerbinCity.Log("Generating Building...");

            GameObject generatedBuilding = new GameObject("KerbinCity_Building1", typeof(MeshRenderer), typeof(MeshFilter), typeof(MeshCollider));
            MeshFilter mf =(MeshFilter)generatedBuilding.GetComponent(typeof(MeshFilter));
            MeshRenderer mr = (MeshRenderer)generatedBuilding.GetComponent(typeof(MeshRenderer));
            Mesh mesh = new Mesh();
            mesh.name = "testMesh";

            KerbinCity.Log("Creating verticies...");
            mesh.Clear();
            mesh.subMeshCount = 1;
            //float hwidth = width/2.0f;
            //float hdepth = depth/2.0f;
            /*Vector3[] vertices = new Vector3[24] 
            {   
                //front
                new Vector3(-hwidth, height, -hdepth), // 0
                new Vector3(hwidth, height, -hdepth),      // 1
                new Vector3(-hwidth, 0.0f, -hdepth),    // 2
                new Vector3(hwidth, 0.0f, -hdepth), // 3
                //back
                new Vector3(-hwidth, height, hdepth),      // 4
                new Vector3(hwidth, height, hdepth),       // 5
                new Vector3(-hwidth, 0.0f, hdepth), // 6
                new Vector3(hwidth, 0.0f, hdepth),       // 7
                //right
                new Vector3(-hwidth, height, hdepth),      // 8
                new Vector3(-hwidth, height, -hdepth), // 9
                new Vector3(-hwidth, 0.0f, hdepth), // 10
                new Vector3(-hwidth, 0.0f, -hdepth),    // 11
                //left
                new Vector3(hwidth, height, hdepth),       // 12
                new Vector3(hwidth, height, -hdepth),      // 13
                new Vector3(hwidth, 0.0f, hdepth),       // 14
                new Vector3(hwidth, 0.0f, -hdepth),   // 15
                //top
                new Vector3(-hwidth, height, hdepth),      // 16
                new Vector3(hwidth, height, hdepth),       // 17
                new Vector3(-hwidth, height, -hdepth), // 18
                new Vector3(hwidth, height, -hdepth),      // 19
                //bottom
                new Vector3(hwidth, 0.0f, -hdepth), // 20
                new Vector3(hwidth, 0.0f, hdepth),       // 21
                new Vector3(-hwidth, 0.0f, -hdepth),    // 22
                new Vector3(-hwidth, 0.0f, hdepth) // 23
            };*/
            Vector3[] vertices = new Vector3[24] 
            {   
                //front
                new Vector3(0, height, 0), // 0
                new Vector3(width, height, 0),      // 1
                new Vector3(0, 0.0f, 0),    // 2
                new Vector3(width, 0.0f, 0), // 3
                //back
                new Vector3(0, height, depth),      // 4
                new Vector3(width, height, depth),       // 5
                new Vector3(0, 0.0f, depth), // 6
                new Vector3(width, 0.0f,depth),       // 7
                //right
                new Vector3(0, height, depth),      // 8
                new Vector3(0, height, 0), // 9
                new Vector3(0, 0.0f, depth), // 10
                new Vector3(0, 0.0f, 0),    // 11
                //left
                new Vector3(width, height, depth),       // 12
                new Vector3(width, height, 0),      // 13
                new Vector3(width, 0.0f, depth),       // 14
                new Vector3(width, 0.0f, 0),   // 15
                //top
                new Vector3(0, height, depth),      // 16
                new Vector3(width, height, depth),       // 17
                new Vector3(0, height, 0), // 18
                new Vector3(width, height, 0),      // 19
                //bottom
                new Vector3(width, 0.0f, 0), // 20
                new Vector3(width, 0.0f, depth),       // 21
                new Vector3(0, 0.0f, 0),    // 22
                new Vector3(0, 0.0f, depth) // 23
            };
            
            mesh.vertices = vertices;

            KerbinCity.Log("Creating triangles...");
            int[] triangles = new int[36]
            {   0, 1, 2,    // front
                2, 1, 3,
                7, 5, 6,    // back
                6, 5, 4,
                8, 9, 10,    // right
                10, 9, 11,
                15, 13, 14,    // left
                14, 13, 12,
                16, 17, 18,    // top
                18, 17, 19,
                20, 21, 22,    // bottom
                22, 21, 23
             };
            mesh.SetTriangles(triangles, 0);
            KerbinCity.Log("Updating normals/bounds...");

            float maxdimension = Math.Max(width, height);
            maxdimension = Math.Max(maxdimension, depth);
            float uvwidth, uvheight, uvdepth;
            uvwidth = width / maxdimension;
            uvheight = height / maxdimension;
            uvdepth = depth / maxdimension;
            mesh.uv = new Vector2[24]
            {
                //front
                new Vector2(0,uvheight),
                new Vector2(uvwidth,uvheight),
                new Vector2(0,0),
                new Vector2(uvwidth,0),
                //back
                new Vector2(0,uvheight),
                new Vector2(uvdepth,uvheight),
                new Vector2(0,0),
                new Vector2(uvdepth,0),
                //right
                new Vector2(0,uvheight),
                new Vector2(uvwidth,uvheight),
                new Vector2(0,0),
                new Vector2(uvwidth,0),
                //left
                new Vector2(0,uvheight),
                new Vector2(uvdepth,uvheight),
                new Vector2(0,0),
                new Vector2(uvdepth,0),
                //top
                new Vector2(0,uvwidth),
                new Vector2(uvdepth,uvwidth),
                new Vector2(0,0),
                new Vector2(uvdepth,0),
                //bottom
                new Vector2(0,uvwidth),
                new Vector2(uvdepth,uvwidth),
                new Vector2(0,0),
                new Vector2(uvdepth,0)
            };
            mf.mesh = mesh;

            
            KerbinCity.Log("setting texture...");
            mr.renderer.sharedMaterials[0] = lowSideTexture[0];
            mesh.RecalculateNormals();
            mesh.RecalculateBounds();

            return generatedBuilding;
        }

    }
}
